1 Gas Data

1.1 Reading in Data

We first read in two data sets called “gas” representing several different values having to due with gas stations in the United States. “Gas” has 72798 observations and 32 total variables.

gas <- read.csv("https://ecoleman451.github.io/website/Data%20Visualization/Datasets/POC.csv")

2 Mapping

2.1 Simple Leaflet Map

In the following map, each point represents the latitude and longitude values for a specific gas station (represented by “xcoord” and “ycoord” respectively in the “gas” data set). Hovering over each point, you will see the “State”, “County”, “Address”, and “Zip Code” for that specific gas station. The results are below:

 gas_samp <- gas %>% sample_n(500)

# Create a leaflet map
gas_map <- leaflet(data = gas_samp) %>%
  addTiles() %>%
  addMarkers(
    lng = ~xcoord,
    lat = ~ycoord,
    popup = ~paste("State: ", STATE, "<br>",
                   "County: ", county, "<br>",
                   "Address: ", ADDRESS, "<br>",
                   "Zip Code: ", ZIPnew)
  )
# Display the map
gas_map

2.2 Leaflet Map

Below, we create a slightly more better leaflet map by using the “radius” and “color” functions. Here, we have the radius fixed at 5, the color fixed at blue, and the markers as circle points instead of location markers. The map is similar to the one above in which each circle point represents a gas station. Hovering over a point will once again give the “State”, “County”, “Address”, and “Zip Code” for that specific gas station.

# Create a leaflet map
gas_map2 <- leaflet(data = gas_samp) %>%
  addTiles() %>%
  setView(lng = mean(gas_samp$xcoord), 
          lat = mean(gas_samp$ycoord), 
          zoom = 13) %>%
  addProviderTiles("Esri.WorldGrayCanvas") %>%
  addCircleMarkers(
    ~xcoord, 
    ~ycoord,
    color = "blue",  # Adjust color as needed
    radius = 5,  # Adjust radius as needed
    stroke = FALSE, 
    fillOpacity = 0.4,
    label = ~paste("State: ", STATE,
                   "County: ", county,
                   "Address: ", ADDRESS,
                   "Zip Code: ", ZIPnew)
  ) %>%
  addLegend(position = "bottomright", 
            colors = "blue",  # Adjust color as needed
            labels = "Gas Station",
            title = "Gas Stations",
            opacity = 0.4)

# Display the map
gas_map2

2.3 Best Map

This time, we create a slightly more complex leaflet map by using the “radius” and “color” functions once again. Here, we have the radius dependent on the number of POCs (points of compromise) in a gas station’s respective ZIP Code. This means that the larger a point appears on the map, the more POCs exist in that station’s ZIP Code. The color is dependent on whether or not a gas station provides “Fuel”, “Service Only” or “Both” fuel and services. So, each of those three categories will have a specific color, representing the services provided at a specific gas station. In the data, we do not get any gas stations that provide “Service Only”, so only the categories “Fuel” and “Both” will appear on the map. The map is similar to the previous maps above in which each circle point represents a gas station. Hovering over a point will once again give the “State”, “County”, “Address”, and “Zip Code” for that specific gas station.

# Create a color palette based on service_or_fuel values
service_palette <- colorFactor(palette = "Set1", domain = gas_samp$service_or_fuel)

# Create the leaflet map
gas_map3 <- leaflet(data = gas_samp) %>%
  addTiles() %>%
  addProviderTiles("Esri.WorldGrayCanvas") %>%
  addCircleMarkers(
    ~xcoord, 
    ~ycoord,
    color = ~service_palette(service_or_fuel),  # Use colorFactor
    radius = gas_samp$ZIPPOC * 10,  # Adjust radius as needed
    stroke = FALSE, 
    fillOpacity = 0.4,
    label = ~paste("State: ", STATE, "<br>",
                   "County: ", county, "<br>",
                   "Address: ", ADDRESS, "<br>",
                   "Zip Code: ", ZIPnew)
  ) %>%
  addLegend(position = "bottomright", 
            colors = service_palette(unique(gas_samp$service_or_fuel)),  # Use unique service_or_fuel values
            labels = unique(gas_samp$service_or_fuel),
            title = "Gas Stations",
            opacity = 0.4)

# Display the map
gas_map3

3 Philly Crime Data

3.1 Reading in Data

We first read in two data sets called “philly” representing several different values having to due with crimes that have occurred in the Philadelphia area since 2015. “Philly” has 15520 observations and 18 total variables.

philly <- read.csv("https://ecoleman451.github.io/website/Data%20Visualization/Datasets/PhillyCrimeSince2015.csv")

3.2 Convert Date Variable to Date Format and Subset

We want to only have 2023 data in our final subset for our map. Since there doesn’t exist a variable for year, we have to create one using the variable date that already exists in the dataset. Once we have our Year variable, we can easily subset the data to focus only on 2023 data. Now, our new data set has 1666 observations and 19 variables, now including Year.

# Convert date variable to date format
philly$date <- as.Date(philly$date, format = "%m/%d/%Y %H:%M")

# Extract year from date variable
philly$year <- format(philly$date, "%Y")

philly <- subset(philly, year=="2023")

3.3 Mapping

Now, we can create a leaflet map looking at fatal versus non-fatal crimes that occured in Philadelphia in the year 2023 by using the “color” function once again. The color is dependent on whether or not a crime was labeled as “Fatal” or “Nonfatal”. So, each category will have a specific color, representing the type of crime (fatal vs. nonfatal) that occurred at the time. The map is similar to the previous maps above in which each circle point represents a specific crime. Hovering over a point will give the “Neighborhood”, “Date”, “Race”, and “Sex”, “Age”, and “Street” for that specific crime.

library(leaflet)
library(dplyr)
# Create color palette for fatal and non-fatal crimes
fatal <- "red"
non_fatal <- "blue"

# Create leaflet map
map <- leaflet(philly) %>%
  addTiles() %>%
  addCircleMarkers(
    ~lng, ~lat,
    color = ifelse(philly$fatal == "Fatal", fatal, non_fatal),
    radius = 5,
    label = ~paste("Neighborhood: ", neighborhood,
                   "Date: ", date,
                   "Race: ", race,
                   "Sex: ", sex,
                   "Age: ", age,
                   "Street: ", street_name),
    labelOptions = labelOptions(
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    colors = c(fatal, non_fatal),
    labels = c("Fatal", "Non-Fatal"),
    title = "Crime Type"
  ) %>%
  addScaleBar() %>%
  addControl(
    html = "<h4>Philadelphia Crime Locations (2015-2024)</h4>",
    position = "topright"
  )

# Display the map
map

Eyeballing the map, we can see that there seems to be a significant amount more of non-fatal crimes versus fatal crimes. Further analysis would be needed to confirm this, of course.

4 Philly Crime Data

4.1 Reading in Data

We first read in a data set representing several different values having to due with the city of Philadelphia. The data, called “Philly”, contains information on Shootings that have occurred in the area and categorizes them as either “Fatal” or “Non-Fatal”. “Philly” has 15555 observations and 21 total variables.

philly  <- na.omit(st_read("https://pengdsci.github.io/STA553VIZ/w08/PhillyShootings.geojson"))
phillyNeighbor  <- st_read("https://pengdsci.github.io/STA553VIZ/w08/Neighborhoods_Philadelphia.geojson")

4.2 Mapping

Now, we can create a leaflet map looking at fatal versus non-fatal crimes that occured in Philadelphia by using the “color” function once again. The color is dependent on whether or not a crime was labeled as “Fatal” or “Nonfatal”. So, each category will have a specific color, representing the type of crime (fatal vs. nonfatal) that occurred at the time. Each crime location is represented with a circle marker. Hovering over a point will display information for “Object ID”, “Year”, “Race”, “Sex”, “Age”, “Wound”, and “Location” for each of the crime points.

# Load required libraries
library(leaflet)
library(sf)

# Convert 'philly' data to sf object
philly_sf <- st_as_sf(philly, coords = c("point_x", "point_y"), crs = 4326)

# Define color palette for fatal and non-fatal crimes
fatal_color <- "red"
non_fatal_color <- "gold"

# Create leaflet map
map <- leaflet() %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(data = phillyNeighbor,
              color = 'skyblue',
              weight = 1)  %>%
  addCircleMarkers(data = philly_sf,
                   ~point_x, ~point_y,
                   color = ifelse(philly$fatal == 1, fatal_color, non_fatal_color),
                   radius = 5,
                   popup = ~paste("Object ID: ", objectid,
                                  "<br>Year: ", year,
                                  "<br>Race: ", race,
                                  "<br>Sex: ", sex,
                                  "<br>Age: ", age,
                                  "<br>Wound: ", wound,
                                  "<br>Location: ", location),
                   labelOptions = labelOptions(
                     direction = "auto"
                   )
  ) %>%
  addLegend(
    position = "bottomright",
    colors = c("red", "gold"),
    labels = c("Fatal", "Non-Fatal"),
    title = "Crime Type"
  ) %>%
  addScaleBar() %>%
  addControl(
    html = "<h4>Philadelphia Crime Locations (2015-2024)</h4>",
    position = "topright"
  ) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  setView(lng = -75.1527, lat = 39.9707, zoom = 11)

# Display the map
map



5 Working Data and Data Loading

Our initial dataset contains Presidential election results from the years 2000, 2004, 2008, 2012, 2016, and 2020. The data, called “election”, has 72617 observations and 12 variables containing information for every state, county, election, candidate winner in each county, and total candidate votes.

Some data cleaning was necessary in order to fix the issue of the county FIPS code, which represents the specific 5 digit code assigned to every county in the United States. The data set initially had some codes containing 4 digits instead of 4 if there contained a “0” as the first digit. For example, Autauga, Alabama’s FIPS code “01001” is recorded as “1001” in the set. This was able to be done through the excel function “TEXT” and was done prior to reading the data in as the set called “election”.

Using this dataset called “election”, we want to split the data so that we have information focusing only from the county level and a dataset for the state level. Both sets include a new variable called “party_percentage” which calculates the percentage of people that picked the winning party in the sets’ respective state/county - “county_data” containing election results by county and “state_data” containing election results by state. Both sets also keep only the winning party in the dataset.

# Load the required library
library(dplyr)

# Read the data
election <- read.csv("https://ecoleman451.github.io/website/Data%20Visualization/Datasets/PresidentialElection2000To2020.csv")

# County-level Data
county_data <- election %>%
  group_by(year, state, county_name) %>%
  mutate(party_percentage = candidatevotes / sum(candidatevotes) * 100) %>%
  filter(party_percentage == max(party_percentage)) %>%
  select(year, state, county_fips, party, candidate, candidatevotes, party_percentage)

# State-level Data
state_data <- election %>%
  group_by(year, state) %>%
  mutate(party_percentage = candidatevotes / sum(candidatevotes) * 100) %>%
  filter(party_percentage == max(party_percentage)) %>%
  select(year, state, party, candidate, candidatevotes, party_percentage)

# Save county-level data to a new CSV file
write.csv(county_data, file = "county_level_data.csv", row.names = FALSE)

# Save state-level data to a new CSV file
write.csv(state_data, file = "state_level_data.csv", row.names = FALSE)

6 Choropleth Map

Now that we’ve created the data set called “county_data” - focusing solely on election results (specifically the winning party) by county level, we can create an interactive Choropleth Map using the data visualization tool Tableau to display presidential election results at the county level. There are two different colors assigned to represent the major political parties (Democrat & Republican) and the map shades in every county to reflect which political party was the winner in a specific election year. A filter is set to change the year(s) in the interactive map. Hover text is also included which appears when hovering over a specific county on the map, containing information for “year”, “state”, “party”, “candidatevotes”, & “party_percentage” for the respective county being hovered over.


LS0tDQp0aXRsZTogIk1hcHBpbmciDQphdXRob3I6ICJFZHdhcmQgQ29sZW1hbiINCmRhdGU6ICJXZXN0IENoZXN0ZXIgVW5pdmVyc2l0eSINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgZWRpdG9yX29wdGlvbnM6DQogICAgICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCi8qIENvbW1vbiBzdHlsZXMgKi8NCmRpdiNUT0MgbGkgew0KICAgIGxpc3Qtc3R5bGU6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOmxpZ2h0Z3JheTsNCiAgICBiYWNrZ3JvdW5kLWltYWdlOm5vbmU7DQogICAgYmFja2dyb3VuZC1yZXBlYXQ6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7DQogICAgZm9udC1mYW1pbHk6IEFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWY7DQogICAgY29sb3I6ICM3ODBjMGM7DQp9DQoNCmgxLnRpdGxlIHsNCiAgICBmb250LXNpemU6IDI0cHg7DQogICAgY29sb3I6IERhcmtSZWQ7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KICAgIGZvbnQtZmFtaWx5OiBBcmlhbCwgSGVsdmV0aWNhLCBzYW5zLXNlcmlmOw0KICAgIGZvbnQtdmFyaWFudC1jYXBzOiBub3JtYWw7DQp9DQpoNC5hdXRob3IgeyANCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IERhcmtSZWQ7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuZGF0ZSB7IA0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogRGFya0JsdWU7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDEgeyANCiAgICBmb250LXNpemU6IDIycHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDIgeyANCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCmgzIHsgDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQpoNCB7IA0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQovKiBUYWIgZmVhdHVyZXMgKi8NCi5uYXY+bGk+YSB7DQogICAgcG9zaXRpb246IHJlbGF0aXZlOw0KICAgIGRpc3BsYXk6IGJsb2NrOw0KICAgIHBhZGRpbmc6IDJweCAxNXB4Ow0KICAgIGNvbG9yOiAjOTkwMDAwOw0KfQ0KLm5hdi1waWxscz5saS5hY3RpdmU+YSwgLm5hdi1waWxscz5saS5hY3RpdmU+YTpob3ZlciwgLm5hdi1waWxscz5saS5hY3RpdmU+YTpmb2N1cyB7DQogICAgY29sb3I6ICNmZmZmZmY7DQogICAgYmFja2dyb3VuZC1jb2xvcjogIzk5MDAwMDsNCn0NCi8qDQpuYXYtcGlsbHM+bGk6bnRoLWNoaWxkKDIpIHsNCiAgICBiYWNrZ3JvdW5kOiBncmVlbjsNCiB9DQogKi8NCmltZyB7DQogICAgYm9yZGVyOiAxcHggc29saWQgIzU1NTsNCn0NCjwvc3R5bGU+DQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCBjb21tZW50PU5BfQ0Kb3B0aW9ucyhyZXBvcyA9IGxpc3QoQ1JBTj0iaHR0cDovL2NyYW4ucnN0dWRpby5jb20vIikpDQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICAgbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoImNvd3Bsb3QiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiY293cGxvdCIpDQogICBsaWJyYXJ5KGNvd3Bsb3QpDQp9DQppZiAoIXJlcXVpcmUoImxhdGV4MmV4cCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJsYXRleDJleHAiKQ0KICAgbGlicmFyeShsYXRleDJleHApDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KICAgbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoImdhcG1pbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJnYXBtaW5kZXIiKQ0KICAgbGlicmFyeShnYXBtaW5kZXIpDQp9DQppZiAoIXJlcXVpcmUoInBuZyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicG5nIikgICAgDQogICAgbGlicmFyeSgicG5nIikNCn0NCmlmICghcmVxdWlyZSgiUkN1cmwiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIlJDdXJsIikgICAgDQogICAgbGlicmFyeSgiUkN1cmwiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjb2xvdXJwaWNrZXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImNvbG91cnBpY2tlciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJjb2xvdXJwaWNrZXIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ2FuaW1hdGUiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdnYW5pbWF0ZSIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnZ2FuaW1hdGUiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnaWZza2kiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdpZnNraSIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnaWZza2kiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJtYWdpY2siKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIm1hZ2ljayIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJtYWdpY2siKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnckRldmljZXMiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdyRGV2aWNlcyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnckRldmljZXMiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJqcGVnIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJqcGVnIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImpwZWciKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ3JpZGdlcyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dyaWRnZXMiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ2dyaWRnZXMiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwbHlyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJwbHlyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInBseXIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ2lyYXBoIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ2lyYXBoIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdnaXJhcGgiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJoaWdoY2hhcnRlciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiaGlnaGNoYXJ0ZXIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiaGlnaGNoYXJ0ZXIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJmb3JlY2FzdCIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZm9yZWNhc3QiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZm9yZWNhc3QiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJsZWFmbGV0IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJsZWFmbGV0IikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImxlYWZsZXQiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJzZiIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygic2YiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgic2YiKQ0KfQ0KIyMgDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgICANCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSBUUlVFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkEpDQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgDQojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4NCmlmICghcmVxdWlyZSgiU3RhdDJEYXRhIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIlN0YXQyRGF0YSIpDQogICBsaWJyYXJ5KFN0YXQyRGF0YSkNCn0NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAgDQogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gVFJVRSwgICANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BKQ0KYGBgDQoNCiMgR2FzIERhdGENCiMjIFJlYWRpbmcgaW4gRGF0YQ0KICBXZSBmaXJzdCByZWFkIGluIHR3byBkYXRhIHNldHMgY2FsbGVkICJnYXMiIHJlcHJlc2VudGluZyBzZXZlcmFsIGRpZmZlcmVudCB2YWx1ZXMgaGF2aW5nIHRvIGR1ZSB3aXRoIGdhcyBzdGF0aW9ucyBpbiB0aGUgVW5pdGVkIFN0YXRlcy4gIkdhcyIgaGFzIDcyNzk4IG9ic2VydmF0aW9ucyBhbmQgMzIgdG90YWwgdmFyaWFibGVzLg0KYGBge3IsIGNvbW1lbnQ9TkF9DQpnYXMgPC0gcmVhZC5jc3YoImh0dHBzOi8vZWNvbGVtYW40NTEuZ2l0aHViLmlvL3dlYnNpdGUvRGF0YSUyMFZpc3VhbGl6YXRpb24vRGF0YXNldHMvUE9DLmNzdiIpDQpgYGANCg0KIyBNYXBwaW5nDQojIyBTaW1wbGUgTGVhZmxldCBNYXANCiAgSW4gdGhlIGZvbGxvd2luZyBtYXAsIGVhY2ggcG9pbnQgcmVwcmVzZW50cyB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSB2YWx1ZXMgZm9yIGEgc3BlY2lmaWMgZ2FzIHN0YXRpb24gKHJlcHJlc2VudGVkIGJ5ICJ4Y29vcmQiIGFuZCAieWNvb3JkIiByZXNwZWN0aXZlbHkgaW4gdGhlICJnYXMiIGRhdGEgc2V0KS4gSG92ZXJpbmcgb3ZlciBlYWNoIHBvaW50LCB5b3Ugd2lsbCBzZWUgdGhlICJTdGF0ZSIsICJDb3VudHkiLCAiQWRkcmVzcyIsIGFuZCAiWmlwIENvZGUiIGZvciB0aGF0IHNwZWNpZmljIGdhcyBzdGF0aW9uLiBUaGUgcmVzdWx0cyBhcmUgYmVsb3c6DQpgYGB7ciwgY29tbWVudD1OQX0NCiBnYXNfc2FtcCA8LSBnYXMgJT4lIHNhbXBsZV9uKDUwMCkNCg0KIyBDcmVhdGUgYSBsZWFmbGV0IG1hcA0KZ2FzX21hcCA8LSBsZWFmbGV0KGRhdGEgPSBnYXNfc2FtcCkgJT4lDQogIGFkZFRpbGVzKCkgJT4lDQogIGFkZE1hcmtlcnMoDQogICAgbG5nID0gfnhjb29yZCwNCiAgICBsYXQgPSB+eWNvb3JkLA0KICAgIHBvcHVwID0gfnBhc3RlKCJTdGF0ZTogIiwgU1RBVEUsICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiQ291bnR5OiAiLCBjb3VudHksICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiQWRkcmVzczogIiwgQUREUkVTUywgIjxicj4iLA0KICAgICAgICAgICAgICAgICAgICJaaXAgQ29kZTogIiwgWklQbmV3KQ0KICApDQojIERpc3BsYXkgdGhlIG1hcA0KZ2FzX21hcA0KYGBgDQoNCiMjIExlYWZsZXQgTWFwDQogIEJlbG93LCB3ZSBjcmVhdGUgYSBzbGlnaHRseSBtb3JlIGJldHRlciBsZWFmbGV0IG1hcCBieSB1c2luZyB0aGUgInJhZGl1cyIgYW5kICJjb2xvciIgZnVuY3Rpb25zLiBIZXJlLCB3ZSBoYXZlIHRoZSByYWRpdXMgZml4ZWQgYXQgNSwgdGhlIGNvbG9yIGZpeGVkIGF0IGJsdWUsIGFuZCB0aGUgbWFya2VycyBhcyBjaXJjbGUgcG9pbnRzIGluc3RlYWQgb2YgbG9jYXRpb24gbWFya2Vycy4gVGhlIG1hcCBpcyBzaW1pbGFyIHRvIHRoZSBvbmUgYWJvdmUgaW4gd2hpY2ggZWFjaCBjaXJjbGUgcG9pbnQgcmVwcmVzZW50cyBhIGdhcyBzdGF0aW9uLiBIb3ZlcmluZyBvdmVyIGEgcG9pbnQgd2lsbCBvbmNlIGFnYWluIGdpdmUgdGhlICJTdGF0ZSIsICJDb3VudHkiLCAiQWRkcmVzcyIsIGFuZCAiWmlwIENvZGUiIGZvciB0aGF0IHNwZWNpZmljIGdhcyBzdGF0aW9uLg0KYGBge3IsIGNvbW1lbnQ9TkF9DQojIENyZWF0ZSBhIGxlYWZsZXQgbWFwDQpnYXNfbWFwMiA8LSBsZWFmbGV0KGRhdGEgPSBnYXNfc2FtcCkgJT4lDQogIGFkZFRpbGVzKCkgJT4lDQogIHNldFZpZXcobG5nID0gbWVhbihnYXNfc2FtcCR4Y29vcmQpLCANCiAgICAgICAgICBsYXQgPSBtZWFuKGdhc19zYW1wJHljb29yZCksIA0KICAgICAgICAgIHpvb20gPSAxMykgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMoIkVzcmkuV29ybGRHcmF5Q2FudmFzIikgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoDQogICAgfnhjb29yZCwgDQogICAgfnljb29yZCwNCiAgICBjb2xvciA9ICJibHVlIiwgICMgQWRqdXN0IGNvbG9yIGFzIG5lZWRlZA0KICAgIHJhZGl1cyA9IDUsICAjIEFkanVzdCByYWRpdXMgYXMgbmVlZGVkDQogICAgc3Ryb2tlID0gRkFMU0UsIA0KICAgIGZpbGxPcGFjaXR5ID0gMC40LA0KICAgIGxhYmVsID0gfnBhc3RlKCJTdGF0ZTogIiwgU1RBVEUsDQogICAgICAgICAgICAgICAgICAgIkNvdW50eTogIiwgY291bnR5LA0KICAgICAgICAgICAgICAgICAgICJBZGRyZXNzOiAiLCBBRERSRVNTLA0KICAgICAgICAgICAgICAgICAgICJaaXAgQ29kZTogIiwgWklQbmV3KQ0KICApICU+JQ0KICBhZGRMZWdlbmQocG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLCANCiAgICAgICAgICAgIGNvbG9ycyA9ICJibHVlIiwgICMgQWRqdXN0IGNvbG9yIGFzIG5lZWRlZA0KICAgICAgICAgICAgbGFiZWxzID0gIkdhcyBTdGF0aW9uIiwNCiAgICAgICAgICAgIHRpdGxlID0gIkdhcyBTdGF0aW9ucyIsDQogICAgICAgICAgICBvcGFjaXR5ID0gMC40KQ0KDQojIERpc3BsYXkgdGhlIG1hcA0KZ2FzX21hcDINCmBgYA0KDQojIyBCZXN0IE1hcA0KIFRoaXMgdGltZSwgd2UgY3JlYXRlIGEgc2xpZ2h0bHkgbW9yZSBjb21wbGV4IGxlYWZsZXQgbWFwIGJ5IHVzaW5nIHRoZSAicmFkaXVzIiBhbmQgImNvbG9yIiBmdW5jdGlvbnMgb25jZSBhZ2Fpbi4gSGVyZSwgd2UgaGF2ZSB0aGUgcmFkaXVzIGRlcGVuZGVudCBvbiB0aGUgbnVtYmVyIG9mIFBPQ3MgKHBvaW50cyBvZiBjb21wcm9taXNlKSBpbiBhIGdhcyBzdGF0aW9uJ3MgcmVzcGVjdGl2ZSBaSVAgQ29kZS4gVGhpcyBtZWFucyB0aGF0IHRoZSBsYXJnZXIgYSBwb2ludCBhcHBlYXJzIG9uIHRoZSBtYXAsIHRoZSBtb3JlIFBPQ3MgZXhpc3QgaW4gdGhhdCBzdGF0aW9uJ3MgWklQIENvZGUuIFRoZSBjb2xvciBpcyBkZXBlbmRlbnQgb24gd2hldGhlciBvciBub3QgYSBnYXMgc3RhdGlvbiBwcm92aWRlcyAiRnVlbCIsICJTZXJ2aWNlIE9ubHkiIG9yICJCb3RoIiBmdWVsIGFuZCBzZXJ2aWNlcy4gU28sIGVhY2ggb2YgdGhvc2UgdGhyZWUgY2F0ZWdvcmllcyB3aWxsIGhhdmUgYSBzcGVjaWZpYyBjb2xvciwgcmVwcmVzZW50aW5nIHRoZSBzZXJ2aWNlcyBwcm92aWRlZCBhdCBhIHNwZWNpZmljIGdhcyBzdGF0aW9uLiBJbiB0aGUgZGF0YSwgd2UgZG8gbm90IGdldCBhbnkgZ2FzIHN0YXRpb25zIHRoYXQgcHJvdmlkZSAiU2VydmljZSBPbmx5Iiwgc28gb25seSB0aGUgY2F0ZWdvcmllcyAiRnVlbCIgYW5kICJCb3RoIiB3aWxsIGFwcGVhciBvbiB0aGUgbWFwLiBUaGUgbWFwIGlzIHNpbWlsYXIgdG8gdGhlIHByZXZpb3VzIG1hcHMgYWJvdmUgaW4gd2hpY2ggZWFjaCBjaXJjbGUgcG9pbnQgcmVwcmVzZW50cyBhIGdhcyBzdGF0aW9uLiBIb3ZlcmluZyBvdmVyIGEgcG9pbnQgd2lsbCBvbmNlIGFnYWluIGdpdmUgdGhlICJTdGF0ZSIsICJDb3VudHkiLCAiQWRkcmVzcyIsIGFuZCAiWmlwIENvZGUiIGZvciB0aGF0IHNwZWNpZmljIGdhcyBzdGF0aW9uLg0KYGBge3IsIGNvbW1lbnQ9TkF9DQojIENyZWF0ZSBhIGNvbG9yIHBhbGV0dGUgYmFzZWQgb24gc2VydmljZV9vcl9mdWVsIHZhbHVlcw0Kc2VydmljZV9wYWxldHRlIDwtIGNvbG9yRmFjdG9yKHBhbGV0dGUgPSAiU2V0MSIsIGRvbWFpbiA9IGdhc19zYW1wJHNlcnZpY2Vfb3JfZnVlbCkNCg0KIyBDcmVhdGUgdGhlIGxlYWZsZXQgbWFwDQpnYXNfbWFwMyA8LSBsZWFmbGV0KGRhdGEgPSBnYXNfc2FtcCkgJT4lDQogIGFkZFRpbGVzKCkgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMoIkVzcmkuV29ybGRHcmF5Q2FudmFzIikgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoDQogICAgfnhjb29yZCwgDQogICAgfnljb29yZCwNCiAgICBjb2xvciA9IH5zZXJ2aWNlX3BhbGV0dGUoc2VydmljZV9vcl9mdWVsKSwgICMgVXNlIGNvbG9yRmFjdG9yDQogICAgcmFkaXVzID0gZ2FzX3NhbXAkWklQUE9DICogMTAsICAjIEFkanVzdCByYWRpdXMgYXMgbmVlZGVkDQogICAgc3Ryb2tlID0gRkFMU0UsIA0KICAgIGZpbGxPcGFjaXR5ID0gMC40LA0KICAgIGxhYmVsID0gfnBhc3RlKCJTdGF0ZTogIiwgU1RBVEUsICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiQ291bnR5OiAiLCBjb3VudHksICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiQWRkcmVzczogIiwgQUREUkVTUywgIjxicj4iLA0KICAgICAgICAgICAgICAgICAgICJaaXAgQ29kZTogIiwgWklQbmV3KQ0KICApICU+JQ0KICBhZGRMZWdlbmQocG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLCANCiAgICAgICAgICAgIGNvbG9ycyA9IHNlcnZpY2VfcGFsZXR0ZSh1bmlxdWUoZ2FzX3NhbXAkc2VydmljZV9vcl9mdWVsKSksICAjIFVzZSB1bmlxdWUgc2VydmljZV9vcl9mdWVsIHZhbHVlcw0KICAgICAgICAgICAgbGFiZWxzID0gdW5pcXVlKGdhc19zYW1wJHNlcnZpY2Vfb3JfZnVlbCksDQogICAgICAgICAgICB0aXRsZSA9ICJHYXMgU3RhdGlvbnMiLA0KICAgICAgICAgICAgb3BhY2l0eSA9IDAuNCkNCg0KIyBEaXNwbGF5IHRoZSBtYXANCmdhc19tYXAzDQpgYGANCg0KDQojIFBoaWxseSBDcmltZSBEYXRhDQojIyBSZWFkaW5nIGluIERhdGENCiAgV2UgZmlyc3QgcmVhZCBpbiB0d28gZGF0YSBzZXRzIGNhbGxlZCAicGhpbGx5IiByZXByZXNlbnRpbmcgc2V2ZXJhbCBkaWZmZXJlbnQgdmFsdWVzIGhhdmluZyB0byBkdWUgd2l0aCBjcmltZXMgdGhhdCBoYXZlIG9jY3VycmVkIGluIHRoZSBQaGlsYWRlbHBoaWEgYXJlYSBzaW5jZSAyMDE1LiAiUGhpbGx5IiBoYXMgMTU1MjAgb2JzZXJ2YXRpb25zIGFuZCAxOCB0b3RhbCB2YXJpYWJsZXMuDQpgYGB7ciwgY29tbWVudD1OQX0NCnBoaWxseSA8LSByZWFkLmNzdigiaHR0cHM6Ly9lY29sZW1hbjQ1MS5naXRodWIuaW8vd2Vic2l0ZS9EYXRhJTIwVmlzdWFsaXphdGlvbi9EYXRhc2V0cy9QaGlsbHlDcmltZVNpbmNlMjAxNS5jc3YiKQ0KYGBgDQojIyBDb252ZXJ0IERhdGUgVmFyaWFibGUgdG8gRGF0ZSBGb3JtYXQgYW5kIFN1YnNldA0KICBXZSB3YW50IHRvIG9ubHkgaGF2ZSAyMDIzIGRhdGEgaW4gb3VyIGZpbmFsIHN1YnNldCBmb3Igb3VyIG1hcC4gU2luY2UgdGhlcmUgZG9lc24ndCBleGlzdCBhIHZhcmlhYmxlIGZvciB5ZWFyLCB3ZSBoYXZlIHRvIGNyZWF0ZSBvbmUgdXNpbmcgdGhlIHZhcmlhYmxlIGRhdGUgdGhhdCBhbHJlYWR5IGV4aXN0cyBpbiB0aGUgZGF0YXNldC4gT25jZSB3ZSBoYXZlIG91ciBZZWFyIHZhcmlhYmxlLCB3ZSBjYW4gZWFzaWx5IHN1YnNldCB0aGUgZGF0YSB0byBmb2N1cyBvbmx5IG9uIDIwMjMgZGF0YS4gTm93LCBvdXIgbmV3IGRhdGEgc2V0IGhhcyAxNjY2IG9ic2VydmF0aW9ucyBhbmQgMTkgdmFyaWFibGVzLCBub3cgaW5jbHVkaW5nIFllYXIuDQpgYGB7ciwgY29tbWVudCA9IE5BfQ0KIyBDb252ZXJ0IGRhdGUgdmFyaWFibGUgdG8gZGF0ZSBmb3JtYXQNCnBoaWxseSRkYXRlIDwtIGFzLkRhdGUocGhpbGx5JGRhdGUsIGZvcm1hdCA9ICIlbS8lZC8lWSAlSDolTSIpDQoNCiMgRXh0cmFjdCB5ZWFyIGZyb20gZGF0ZSB2YXJpYWJsZQ0KcGhpbGx5JHllYXIgPC0gZm9ybWF0KHBoaWxseSRkYXRlLCAiJVkiKQ0KDQpwaGlsbHkgPC0gc3Vic2V0KHBoaWxseSwgeWVhcj09IjIwMjMiKQ0KYGBgDQoNCiMjIE1hcHBpbmcNCiAgTm93LCB3ZSBjYW4gY3JlYXRlIGEgbGVhZmxldCBtYXAgbG9va2luZyBhdCBmYXRhbCB2ZXJzdXMgbm9uLWZhdGFsIGNyaW1lcyB0aGF0IG9jY3VyZWQgaW4gUGhpbGFkZWxwaGlhIGluIHRoZSB5ZWFyIDIwMjMgYnkgdXNpbmcgdGhlICJjb2xvciIgZnVuY3Rpb24gb25jZSBhZ2Fpbi4gVGhlIGNvbG9yIGlzIGRlcGVuZGVudCBvbiB3aGV0aGVyIG9yIG5vdCBhIGNyaW1lIHdhcyBsYWJlbGVkIGFzICJGYXRhbCIgb3IgIk5vbmZhdGFsIi4gU28sIGVhY2ggY2F0ZWdvcnkgd2lsbCBoYXZlIGEgc3BlY2lmaWMgY29sb3IsIHJlcHJlc2VudGluZyB0aGUgdHlwZSBvZiBjcmltZSAoZmF0YWwgdnMuIG5vbmZhdGFsKSB0aGF0IG9jY3VycmVkIGF0IHRoZSB0aW1lLiBUaGUgbWFwIGlzIHNpbWlsYXIgdG8gdGhlIHByZXZpb3VzIG1hcHMgYWJvdmUgaW4gd2hpY2ggZWFjaCBjaXJjbGUgcG9pbnQgcmVwcmVzZW50cyBhIHNwZWNpZmljIGNyaW1lLiBIb3ZlcmluZyBvdmVyIGEgcG9pbnQgd2lsbCBnaXZlIHRoZSAiTmVpZ2hib3Job29kIiwgIkRhdGUiLCAiUmFjZSIsIGFuZCAiU2V4IiwgIkFnZSIsIGFuZCAiU3RyZWV0IiBmb3IgdGhhdCBzcGVjaWZpYyBjcmltZS4NCmBgYHtyLCBjb21tZW50PU5BfQ0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShkcGx5cikNCiMgQ3JlYXRlIGNvbG9yIHBhbGV0dGUgZm9yIGZhdGFsIGFuZCBub24tZmF0YWwgY3JpbWVzDQpmYXRhbCA8LSAicmVkIg0Kbm9uX2ZhdGFsIDwtICJibHVlIg0KDQojIENyZWF0ZSBsZWFmbGV0IG1hcA0KbWFwIDwtIGxlYWZsZXQocGhpbGx5KSAlPiUNCiAgYWRkVGlsZXMoKSAlPiUNCiAgYWRkQ2lyY2xlTWFya2VycygNCiAgICB+bG5nLCB+bGF0LA0KICAgIGNvbG9yID0gaWZlbHNlKHBoaWxseSRmYXRhbCA9PSAiRmF0YWwiLCBmYXRhbCwgbm9uX2ZhdGFsKSwNCiAgICByYWRpdXMgPSA1LA0KICAgIGxhYmVsID0gfnBhc3RlKCJOZWlnaGJvcmhvb2Q6ICIsIG5laWdoYm9yaG9vZCwNCiAgICAgICAgICAgICAgICAgICAiRGF0ZTogIiwgZGF0ZSwNCiAgICAgICAgICAgICAgICAgICAiUmFjZTogIiwgcmFjZSwNCiAgICAgICAgICAgICAgICAgICAiU2V4OiAiLCBzZXgsDQogICAgICAgICAgICAgICAgICAgIkFnZTogIiwgYWdlLA0KICAgICAgICAgICAgICAgICAgICJTdHJlZXQ6ICIsIHN0cmVldF9uYW1lKSwNCiAgICBsYWJlbE9wdGlvbnMgPSBsYWJlbE9wdGlvbnMoDQogICAgICBkaXJlY3Rpb24gPSAiYXV0byINCiAgICApDQogICkgJT4lDQogIGFkZExlZ2VuZCgNCiAgICBwb3NpdGlvbiA9ICJib3R0b21yaWdodCIsDQogICAgY29sb3JzID0gYyhmYXRhbCwgbm9uX2ZhdGFsKSwNCiAgICBsYWJlbHMgPSBjKCJGYXRhbCIsICJOb24tRmF0YWwiKSwNCiAgICB0aXRsZSA9ICJDcmltZSBUeXBlIg0KICApICU+JQ0KICBhZGRTY2FsZUJhcigpICU+JQ0KICBhZGRDb250cm9sKA0KICAgIGh0bWwgPSAiPGg0PlBoaWxhZGVscGhpYSBDcmltZSBMb2NhdGlvbnMgKDIwMTUtMjAyNCk8L2g0PiIsDQogICAgcG9zaXRpb24gPSAidG9wcmlnaHQiDQogICkNCg0KIyBEaXNwbGF5IHRoZSBtYXANCm1hcA0KYGBgDQogIEV5ZWJhbGxpbmcgdGhlIG1hcCwgd2UgY2FuIHNlZSB0aGF0IHRoZXJlIHNlZW1zIHRvIGJlIGEgc2lnbmlmaWNhbnQgYW1vdW50IG1vcmUgb2Ygbm9uLWZhdGFsIGNyaW1lcyB2ZXJzdXMgZmF0YWwgY3JpbWVzLiBGdXJ0aGVyIGFuYWx5c2lzIHdvdWxkIGJlIG5lZWRlZCB0byBjb25maXJtIHRoaXMsIG9mIGNvdXJzZS4NCg0KDQojIFBoaWxseSBDcmltZSBEYXRhDQojIyBSZWFkaW5nIGluIERhdGENCiAgV2UgZmlyc3QgcmVhZCBpbiBhIGRhdGEgc2V0IHJlcHJlc2VudGluZyBzZXZlcmFsIGRpZmZlcmVudCB2YWx1ZXMgaGF2aW5nIHRvIGR1ZSB3aXRoIHRoZSBjaXR5IG9mIFBoaWxhZGVscGhpYS4gVGhlIGRhdGEsIGNhbGxlZCAiUGhpbGx5IiwgY29udGFpbnMgaW5mb3JtYXRpb24gb24gU2hvb3RpbmdzIHRoYXQgaGF2ZSBvY2N1cnJlZCBpbiB0aGUgYXJlYSBhbmQgY2F0ZWdvcml6ZXMgdGhlbSBhcyBlaXRoZXIgIkZhdGFsIiBvciAiTm9uLUZhdGFsIi4gIlBoaWxseSIgaGFzIDE1NTU1IG9ic2VydmF0aW9ucyBhbmQgMjEgdG90YWwgdmFyaWFibGVzLg0KYGBge3IsIGNvbW1lbnQ9TkEsIHJlc3VsdHM9RkFMU0V9DQpwaGlsbHkgIDwtIG5hLm9taXQoc3RfcmVhZCgiaHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUzVklaL3cwOC9QaGlsbHlTaG9vdGluZ3MuZ2VvanNvbiIpKQ0KcGhpbGx5TmVpZ2hib3IgIDwtIHN0X3JlYWQoImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1M1ZJWi93MDgvTmVpZ2hib3Job29kc19QaGlsYWRlbHBoaWEuZ2VvanNvbiIpDQpgYGANCg0KIyMgTWFwcGluZw0KICBOb3csIHdlIGNhbiBjcmVhdGUgYSBsZWFmbGV0IG1hcCBsb29raW5nIGF0IGZhdGFsIHZlcnN1cyBub24tZmF0YWwgY3JpbWVzIHRoYXQgb2NjdXJlZCBpbiBQaGlsYWRlbHBoaWEgYnkgdXNpbmcgdGhlICJjb2xvciIgZnVuY3Rpb24gb25jZSBhZ2Fpbi4gVGhlIGNvbG9yIGlzIGRlcGVuZGVudCBvbiB3aGV0aGVyIG9yIG5vdCBhIGNyaW1lIHdhcyBsYWJlbGVkIGFzICJGYXRhbCIgb3IgIk5vbmZhdGFsIi4gU28sIGVhY2ggY2F0ZWdvcnkgd2lsbCBoYXZlIGEgc3BlY2lmaWMgY29sb3IsIHJlcHJlc2VudGluZyB0aGUgdHlwZSBvZiBjcmltZSAoZmF0YWwgdnMuIG5vbmZhdGFsKSB0aGF0IG9jY3VycmVkIGF0IHRoZSB0aW1lLiBFYWNoIGNyaW1lIGxvY2F0aW9uIGlzIHJlcHJlc2VudGVkIHdpdGggYSBjaXJjbGUgbWFya2VyLiBIb3ZlcmluZyBvdmVyIGEgcG9pbnQgd2lsbCBkaXNwbGF5IGluZm9ybWF0aW9uIGZvciAiT2JqZWN0IElEIiwgIlllYXIiLCAiUmFjZSIsICJTZXgiLCAiQWdlIiwgIldvdW5kIiwgYW5kICJMb2NhdGlvbiIgZm9yIGVhY2ggb2YgdGhlIGNyaW1lIHBvaW50cy4NCmBgYHtyfQ0KIyBMb2FkIHJlcXVpcmVkIGxpYnJhcmllcw0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShzZikNCg0KIyBDb252ZXJ0ICdwaGlsbHknIGRhdGEgdG8gc2Ygb2JqZWN0DQpwaGlsbHlfc2YgPC0gc3RfYXNfc2YocGhpbGx5LCBjb29yZHMgPSBjKCJwb2ludF94IiwgInBvaW50X3kiKSwgY3JzID0gNDMyNikNCg0KIyBEZWZpbmUgY29sb3IgcGFsZXR0ZSBmb3IgZmF0YWwgYW5kIG5vbi1mYXRhbCBjcmltZXMNCmZhdGFsX2NvbG9yIDwtICJyZWQiDQpub25fZmF0YWxfY29sb3IgPC0gImdvbGQiDQoNCiMgQ3JlYXRlIGxlYWZsZXQgbWFwDQptYXAgPC0gbGVhZmxldCgpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRFc3JpLldvcmxkR3JheUNhbnZhcykgJT4lDQogIGFkZFBvbHlnb25zKGRhdGEgPSBwaGlsbHlOZWlnaGJvciwNCiAgICAgICAgICAgICAgY29sb3IgPSAnc2t5Ymx1ZScsDQogICAgICAgICAgICAgIHdlaWdodCA9IDEpICAlPiUNCiAgYWRkQ2lyY2xlTWFya2VycyhkYXRhID0gcGhpbGx5X3NmLA0KICAgICAgICAgICAgICAgICAgIH5wb2ludF94LCB+cG9pbnRfeSwNCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGlmZWxzZShwaGlsbHkkZmF0YWwgPT0gMSwgZmF0YWxfY29sb3IsIG5vbl9mYXRhbF9jb2xvciksDQogICAgICAgICAgICAgICAgICAgcmFkaXVzID0gNSwNCiAgICAgICAgICAgICAgICAgICBwb3B1cCA9IH5wYXN0ZSgiT2JqZWN0IElEOiAiLCBvYmplY3RpZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPlllYXI6ICIsIHllYXIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5SYWNlOiAiLCByYWNlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+U2V4OiAiLCBzZXgsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5BZ2U6ICIsIGFnZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPldvdW5kOiAiLCB3b3VuZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPkxvY2F0aW9uOiAiLCBsb2NhdGlvbiksDQogICAgICAgICAgICAgICAgICAgbGFiZWxPcHRpb25zID0gbGFiZWxPcHRpb25zKA0KICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImF1dG8iDQogICAgICAgICAgICAgICAgICAgKQ0KICApICU+JQ0KICBhZGRMZWdlbmQoDQogICAgcG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLA0KICAgIGNvbG9ycyA9IGMoInJlZCIsICJnb2xkIiksDQogICAgbGFiZWxzID0gYygiRmF0YWwiLCAiTm9uLUZhdGFsIiksDQogICAgdGl0bGUgPSAiQ3JpbWUgVHlwZSINCiAgKSAlPiUNCiAgYWRkU2NhbGVCYXIoKSAlPiUNCiAgYWRkQ29udHJvbCgNCiAgICBodG1sID0gIjxoND5QaGlsYWRlbHBoaWEgQ3JpbWUgTG9jYXRpb25zICgyMDE1LTIwMjQpPC9oND4iLA0KICAgIHBvc2l0aW9uID0gInRvcHJpZ2h0Ig0KICApICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRFc3JpLldvcmxkR3JheUNhbnZhcykgJT4lDQogIHNldFZpZXcobG5nID0gLTc1LjE1MjcsIGxhdCA9IDM5Ljk3MDcsIHpvb20gPSAxMSkNCg0KIyBEaXNwbGF5IHRoZSBtYXANCm1hcA0KYGBgDQoNCg0KXA0KDQpcDQoNCiMgV29ya2luZyBEYXRhIGFuZCBEYXRhIExvYWRpbmcNCk91ciBpbml0aWFsIGRhdGFzZXQgY29udGFpbnMgUHJlc2lkZW50aWFsIGVsZWN0aW9uIHJlc3VsdHMgZnJvbSB0aGUgeWVhcnMgMjAwMCwgMjAwNCwgMjAwOCwgMjAxMiwgMjAxNiwgYW5kIDIwMjAuIFRoZSBkYXRhLCBjYWxsZWQgImVsZWN0aW9uIiwgaGFzIDcyNjE3IG9ic2VydmF0aW9ucyBhbmQgMTIgdmFyaWFibGVzIGNvbnRhaW5pbmcgaW5mb3JtYXRpb24gZm9yIGV2ZXJ5IHN0YXRlLCBjb3VudHksIGVsZWN0aW9uLCBjYW5kaWRhdGUgd2lubmVyIGluIGVhY2ggY291bnR5LCBhbmQgdG90YWwgY2FuZGlkYXRlIHZvdGVzLiANCg0KU29tZSBkYXRhIGNsZWFuaW5nIHdhcyBuZWNlc3NhcnkgaW4gb3JkZXIgdG8gZml4IHRoZSBpc3N1ZSBvZiB0aGUgY291bnR5IEZJUFMgY29kZSwgd2hpY2ggcmVwcmVzZW50cyB0aGUgc3BlY2lmaWMgNSBkaWdpdCBjb2RlIGFzc2lnbmVkIHRvIGV2ZXJ5IGNvdW50eSBpbiB0aGUgVW5pdGVkIFN0YXRlcy4gVGhlIGRhdGEgc2V0IGluaXRpYWxseSBoYWQgc29tZSBjb2RlcyBjb250YWluaW5nIDQgZGlnaXRzIGluc3RlYWQgb2YgNCBpZiB0aGVyZSBjb250YWluZWQgYSAiMCIgYXMgdGhlIGZpcnN0IGRpZ2l0LiBGb3IgZXhhbXBsZSwgQXV0YXVnYSwgQWxhYmFtYSdzIEZJUFMgY29kZSAiMDEwMDEiIGlzIHJlY29yZGVkIGFzICIxMDAxIiBpbiB0aGUgc2V0LiBUaGlzIHdhcyBhYmxlIHRvIGJlIGRvbmUgdGhyb3VnaCB0aGUgZXhjZWwgZnVuY3Rpb24gIlRFWFQiIGFuZCB3YXMgZG9uZSBwcmlvciB0byByZWFkaW5nIHRoZSBkYXRhIGluIGFzIHRoZSBzZXQgY2FsbGVkICJlbGVjdGlvbiIuDQoNClVzaW5nIHRoaXMgZGF0YXNldCBjYWxsZWQgImVsZWN0aW9uIiwgd2Ugd2FudCB0byBzcGxpdCB0aGUgZGF0YSBzbyB0aGF0IHdlIGhhdmUgaW5mb3JtYXRpb24gZm9jdXNpbmcgb25seSBmcm9tIHRoZSBjb3VudHkgbGV2ZWwgYW5kIGEgZGF0YXNldCBmb3IgdGhlIHN0YXRlIGxldmVsLiBCb3RoIHNldHMgaW5jbHVkZSBhIG5ldyB2YXJpYWJsZSBjYWxsZWQgInBhcnR5X3BlcmNlbnRhZ2UiIHdoaWNoIGNhbGN1bGF0ZXMgdGhlIHBlcmNlbnRhZ2Ugb2YgcGVvcGxlIHRoYXQgcGlja2VkIHRoZSB3aW5uaW5nIHBhcnR5IGluIHRoZSBzZXRzJyByZXNwZWN0aXZlIHN0YXRlL2NvdW50eSAtICJjb3VudHlfZGF0YSIgY29udGFpbmluZyBlbGVjdGlvbiByZXN1bHRzIGJ5IGNvdW50eSBhbmQgInN0YXRlX2RhdGEiIGNvbnRhaW5pbmcgZWxlY3Rpb24gcmVzdWx0cyBieSBzdGF0ZS4gQm90aCBzZXRzIGFsc28ga2VlcCBvbmx5IHRoZSB3aW5uaW5nIHBhcnR5IGluIHRoZSBkYXRhc2V0Lg0KYGBge3J9DQojIExvYWQgdGhlIHJlcXVpcmVkIGxpYnJhcnkNCmxpYnJhcnkoZHBseXIpDQoNCiMgUmVhZCB0aGUgZGF0YQ0KZWxlY3Rpb24gPC0gcmVhZC5jc3YoImh0dHBzOi8vZWNvbGVtYW40NTEuZ2l0aHViLmlvL3dlYnNpdGUvRGF0YSUyMFZpc3VhbGl6YXRpb24vRGF0YXNldHMvUHJlc2lkZW50aWFsRWxlY3Rpb24yMDAwVG8yMDIwLmNzdiIpDQoNCiMgQ291bnR5LWxldmVsIERhdGENCmNvdW50eV9kYXRhIDwtIGVsZWN0aW9uICU+JQ0KICBncm91cF9ieSh5ZWFyLCBzdGF0ZSwgY291bnR5X25hbWUpICU+JQ0KICBtdXRhdGUocGFydHlfcGVyY2VudGFnZSA9IGNhbmRpZGF0ZXZvdGVzIC8gc3VtKGNhbmRpZGF0ZXZvdGVzKSAqIDEwMCkgJT4lDQogIGZpbHRlcihwYXJ0eV9wZXJjZW50YWdlID09IG1heChwYXJ0eV9wZXJjZW50YWdlKSkgJT4lDQogIHNlbGVjdCh5ZWFyLCBzdGF0ZSwgY291bnR5X2ZpcHMsIHBhcnR5LCBjYW5kaWRhdGUsIGNhbmRpZGF0ZXZvdGVzLCBwYXJ0eV9wZXJjZW50YWdlKQ0KDQojIFN0YXRlLWxldmVsIERhdGENCnN0YXRlX2RhdGEgPC0gZWxlY3Rpb24gJT4lDQogIGdyb3VwX2J5KHllYXIsIHN0YXRlKSAlPiUNCiAgbXV0YXRlKHBhcnR5X3BlcmNlbnRhZ2UgPSBjYW5kaWRhdGV2b3RlcyAvIHN1bShjYW5kaWRhdGV2b3RlcykgKiAxMDApICU+JQ0KICBmaWx0ZXIocGFydHlfcGVyY2VudGFnZSA9PSBtYXgocGFydHlfcGVyY2VudGFnZSkpICU+JQ0KICBzZWxlY3QoeWVhciwgc3RhdGUsIHBhcnR5LCBjYW5kaWRhdGUsIGNhbmRpZGF0ZXZvdGVzLCBwYXJ0eV9wZXJjZW50YWdlKQ0KDQojIFNhdmUgY291bnR5LWxldmVsIGRhdGEgdG8gYSBuZXcgQ1NWIGZpbGUNCndyaXRlLmNzdihjb3VudHlfZGF0YSwgZmlsZSA9ICJjb3VudHlfbGV2ZWxfZGF0YS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCg0KIyBTYXZlIHN0YXRlLWxldmVsIGRhdGEgdG8gYSBuZXcgQ1NWIGZpbGUNCndyaXRlLmNzdihzdGF0ZV9kYXRhLCBmaWxlID0gInN0YXRlX2xldmVsX2RhdGEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCg0KIyBDaG9yb3BsZXRoIE1hcA0KTm93IHRoYXQgd2UndmUgY3JlYXRlZCB0aGUgZGF0YSBzZXQgY2FsbGVkICJjb3VudHlfZGF0YSIgLSBmb2N1c2luZyBzb2xlbHkgb24gZWxlY3Rpb24gcmVzdWx0cyAoc3BlY2lmaWNhbGx5IHRoZSB3aW5uaW5nIHBhcnR5KSBieSBjb3VudHkgbGV2ZWwsIHdlIGNhbiBjcmVhdGUgYW4gaW50ZXJhY3RpdmUgQ2hvcm9wbGV0aCBNYXAgdXNpbmcgdGhlIGRhdGEgdmlzdWFsaXphdGlvbiB0b29sIFRhYmxlYXUgdG8gZGlzcGxheSBwcmVzaWRlbnRpYWwgZWxlY3Rpb24gcmVzdWx0cyBhdCB0aGUgY291bnR5IGxldmVsLiBUaGVyZSBhcmUgdHdvIGRpZmZlcmVudCBjb2xvcnMgYXNzaWduZWQgdG8gcmVwcmVzZW50IHRoZSBtYWpvciBwb2xpdGljYWwgcGFydGllcyAoRGVtb2NyYXQgJiBSZXB1YmxpY2FuKSBhbmQgdGhlIG1hcCBzaGFkZXMgaW4gZXZlcnkgY291bnR5IHRvIHJlZmxlY3Qgd2hpY2ggcG9saXRpY2FsIHBhcnR5IHdhcyB0aGUgd2lubmVyIGluIGEgc3BlY2lmaWMgZWxlY3Rpb24geWVhci4gQSBmaWx0ZXIgaXMgc2V0IHRvIGNoYW5nZSB0aGUgeWVhcihzKSBpbiB0aGUgaW50ZXJhY3RpdmUgbWFwLiBIb3ZlciB0ZXh0IGlzIGFsc28gaW5jbHVkZWQgd2hpY2ggYXBwZWFycyB3aGVuIGhvdmVyaW5nIG92ZXIgYSBzcGVjaWZpYyBjb3VudHkgb24gdGhlIG1hcCwgY29udGFpbmluZyBpbmZvcm1hdGlvbiBmb3IgInllYXIiLCAic3RhdGUiLCAicGFydHkiLCAiY2FuZGlkYXRldm90ZXMiLCAmICJwYXJ0eV9wZXJjZW50YWdlIiBmb3IgdGhlIHJlc3BlY3RpdmUgY291bnR5IGJlaW5nIGhvdmVyZWQgb3Zlci4NClwNCjx0YWJsZSBib3JkZXIgPSAwIGJvcmRlcmNvbG9yPSJkYXJrZ3JlZW4iIGJnY29sb3I9JyNmNmY2ZjYnICB3aWR0aD0xMDAlICBhbGlnbiA9IGNlbnRlcj4NCjx0cj4NCjx0ZD4NCjxkaXYgY2xhc3M9J3RhYmxlYXVQbGFjZWhvbGRlcicgaWQ9J3ZpejE3MTIyNjkyOTQwMjAnIHN0eWxlPSdwb3NpdGlvbjogcmVsYXRpdmUnPjxub3NjcmlwdD48YSBocmVmPScjJz48aW1nIGFsdD0nQ2hvcm9wbGV0aCBNYXAgJyBzcmM9J2h0dHBzOiYjNDc7JiM0NztwdWJsaWMudGFibGVhdS5jb20mIzQ3O3N0YXRpYyYjNDc7aW1hZ2VzJiM0NztDaCYjNDc7Q2hvcm9wbGV0aF9NYXBfRmluYWwmIzQ3O1NoZWV0MSYjNDc7MV9yc3MucG5nJyBzdHlsZT0nYm9yZGVyOiBub25lJyAvPjwvYT48L25vc2NyaXB0PjxvYmplY3QgY2xhc3M9J3RhYmxlYXVWaXonICBzdHlsZT0nZGlzcGxheTpub25lOyc+PHBhcmFtIG5hbWU9J2hvc3RfdXJsJyB2YWx1ZT0naHR0cHMlM0ElMkYlMkZwdWJsaWMudGFibGVhdS5jb20lMkYnIC8+IDxwYXJhbSBuYW1lPSdlbWJlZF9jb2RlX3ZlcnNpb24nIHZhbHVlPSczJyAvPiA8cGFyYW0gbmFtZT0nc2l0ZV9yb290JyB2YWx1ZT0nJyAvPjxwYXJhbSBuYW1lPSduYW1lJyB2YWx1ZT0nQ2hvcm9wbGV0aF9NYXBfRmluYWwmIzQ3O1NoZWV0MScgLz48cGFyYW0gbmFtZT0ndGFicycgdmFsdWU9J25vJyAvPjxwYXJhbSBuYW1lPSd0b29sYmFyJyB2YWx1ZT0neWVzJyAvPjxwYXJhbSBuYW1lPSdzdGF0aWNfaW1hZ2UnIHZhbHVlPSdodHRwczomIzQ3OyYjNDc7cHVibGljLnRhYmxlYXUuY29tJiM0NztzdGF0aWMmIzQ3O2ltYWdlcyYjNDc7Q2gmIzQ3O0Nob3JvcGxldGhfTWFwX0ZpbmFsJiM0NztTaGVldDEmIzQ3OzEucG5nJyAvPiA8cGFyYW0gbmFtZT0nYW5pbWF0ZV90cmFuc2l0aW9uJyB2YWx1ZT0neWVzJyAvPjxwYXJhbSBuYW1lPSdkaXNwbGF5X3N0YXRpY19pbWFnZScgdmFsdWU9J3llcycgLz48cGFyYW0gbmFtZT0nZGlzcGxheV9zcGlubmVyJyB2YWx1ZT0neWVzJyAvPjxwYXJhbSBuYW1lPSdkaXNwbGF5X292ZXJsYXknIHZhbHVlPSd5ZXMnIC8+PHBhcmFtIG5hbWU9J2Rpc3BsYXlfY291bnQnIHZhbHVlPSd5ZXMnIC8+PHBhcmFtIG5hbWU9J2xhbmd1YWdlJyB2YWx1ZT0nZW4tVVMnIC8+PHBhcmFtIG5hbWU9J2ZpbHRlcicgdmFsdWU9J3B1Ymxpc2g9eWVzJyAvPjwvb2JqZWN0PjwvZGl2PiAgICAgICAgICAgICAgICANCjxzY3JpcHQgdHlwZT0ndGV4dC9qYXZhc2NyaXB0Jz4gICAgICAgICAgICAgICAgICAgIHZhciBkaXZFbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZpejE3MTIyNjkyOTQwMjAnKTsgICAgICAgICAgICAgICAgICAgIHZhciB2aXpFbGVtZW50ID0gZGl2RWxlbWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnb2JqZWN0JylbMF07ICAgICAgICAgICAgICAgICAgICB2aXpFbGVtZW50LnN0eWxlLndpZHRoPScxMDAlJzt2aXpFbGVtZW50LnN0eWxlLmhlaWdodD0oZGl2RWxlbWVudC5vZmZzZXRXaWR0aCowLjc1KSsncHgnOyAgICAgICAgICAgICAgICAgICAgdmFyIHNjcmlwdEVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTsgICAgICAgICAgICAgICAgICAgIHNjcmlwdEVsZW1lbnQuc3JjID0gJ2h0dHBzOi8vcHVibGljLnRhYmxlYXUuY29tL2phdmFzY3JpcHRzL2FwaS92aXpfdjEuanMnOyAgICAgICAgICAgICAgICAgICAgdml6RWxlbWVudC5wYXJlbnROb2RlLmluc2VydEJlZm9yZShzY3JpcHRFbGVtZW50LCB2aXpFbGVtZW50KTsgICAgICAgICAgICAgICAgDQo8L3NjcmlwdD4NCjwvdGQ+DQo8L3RyPg0KPC90YWJsZT4gICAgICAgICAgICANClw=